package xiongwei.jiang.service.impl;

import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import xiongwei.jiang.constant.UploadTempType;
import xiongwei.jiang.constant.WXWorkApiConstants;
import xiongwei.jiang.exception.WXWorkException;
import xiongwei.jiang.model.dto.request.*;
import xiongwei.jiang.model.dto.response.*;
import xiongwei.jiang.property.WXWorkProperties;
import xiongwei.jiang.service.EhcacheService;
import xiongwei.jiang.service.WXWorkService;
import xiongwei.jiang.util.MathUtils;
import xiongwei.jiang.util.StringUtils;
import xiongwei.jiang.wxwork.WXBizMsgCrypt;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * 企业微信服务接口实现类
 *
 * @author 天之蓝
 */
@EnableConfigurationProperties(value = WXWorkProperties.class)
@Service
public class WXWorkServiceImpl implements WXWorkService, InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(WXWorkServiceImpl.class);

    public static final Gson GSON = new Gson();

    public static final String WXWORK_ALIAS = "ehcache:wxwork:7200s";

    @Resource
    private EhcacheService ehcacheService;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private WXWorkProperties wxWorkProperties;

    @Override
    public void afterPropertiesSet() {
        if (StringUtils.isBlank(wxWorkProperties.getCorpId())) {
            throw new WXWorkException("wxwork 初始化异常：缺少重要参数（参数配置请参考 README.md，项目地址：https://gitee.com/xiongwei-jiang/wxwork-spring-boot-starter/）");
        }
    }

    @Override
    public UserIdResponseDTO getUserId(Integer appIndex, String code) {
        String url = String.format(WXWorkApiConstants.GET_USER_ID_URL, getTokenCache(appIndex), code);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.debug("请求 [{}] getUserId 收到数据：{}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), UserIdResponseDTO.class);
    }

    @Override
    public UserInfoResponseDTO getUserInfo(Integer appIndex, String userId) {
        String url = String.format(WXWorkApiConstants.GET_USER_INFO_URL, getTokenCache(appIndex), userId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.debug("请求 [{}] getUserInfo 收到数据：{}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), UserInfoResponseDTO.class);
    }

    @Override
    public DepartmentInfoResponseDTO getDepartmentInfo(Integer appIndex, Integer departmentId) {
        String url = String.format(WXWorkApiConstants.GET_DEPARTMENT_INFO_URL, getTokenCache(appIndex), departmentId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.debug("请求 [{}] getDepartmentInfo 收到数据：{}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), DepartmentInfoResponseDTO.class);
    }

    @Override
    public DepartmentUserResponseDTO getDepartmentUser(Integer appIndex, Integer departmentId, Integer fetchChild) {
        String url = String.format(WXWorkApiConstants.GET_DEPARTMENT_USER_URL, getTokenCache(appIndex), departmentId, fetchChild);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.debug("请求 [{}] getDepartmentUser 收到数据：{}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), DepartmentUserResponseDTO.class);
    }

    @Override
    public TagInfoResponseDTO getTagInfo() {
        String url = String.format(WXWorkApiConstants.GET_TAG_INFO_URL, getTokenCache(-1));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.debug("请求 [-1] getTagInfo 收到数据：{}", responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), TagInfoResponseDTO.class);
    }

    @Override
    public TagUserResponseDTO getTagUser(Integer tagId) {
        String url = String.format(WXWorkApiConstants.GET_TAG_USER_URL, getTokenCache(-1), tagId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.debug("请求 [-1] getTagUser 收到数据：{}", responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), TagUserResponseDTO.class);
    }

    @Override
    public AppMsgResponseDTO sendAppTextMsg(Integer appIndex, AppTextMsgRequestDTO object) {
        HttpEntity<String> responseEntity = sendAppMsg(appIndex, GSON.toJson(object));
        String to = Arrays.toString(new String[]{object.getTouser(), object.getToparty(), object.getTotag()});
        LOGGER.debug("发送 [{}] 应用文本消息通知 {} 收到数据：{}", appIndex, to, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), AppMsgResponseDTO.class);
    }

    @Override
    public AppMsgResponseDTO sendAppMarkdownMsg(Integer appIndex, AppMarkdownMsgRequestDTO object) {
        HttpEntity<String> responseEntity = sendAppMsg(appIndex, GSON.toJson(object));
        String to = Arrays.toString(new String[]{object.getTouser(), object.getToparty(), object.getTotag()});
        LOGGER.debug("发送 [{}] 应用 markdown 消息通知 {} 收到数据：{}", appIndex, to, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), AppMsgResponseDTO.class);
    }

    /**
     * 发送应用消息
     *
     * @param appIndex 索引
     * @param json     JSON
     * @return {@link HttpEntity}
     * @author 天之蓝
     */
    public HttpEntity<String> sendAppMsg(Integer appIndex, String json) {
        String url = String.format(WXWorkApiConstants.POST_SEND_MSG_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(json, requestHeaders);
        return restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
    }

    @Override
    public AppUploadTempResponseDTO uploadTemp(Integer appIndex, UploadTempType tempType, String filePath) throws IOException {
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>(2);
        FileSystemResource fileSystemResource = new FileSystemResource(filePath);
        form.add("file", fileSystemResource);
        String url = String.format(WXWorkApiConstants.POST_UPLOAD_TEMP_URL, getTokenCache(appIndex), tempType.getCode());
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        requestHeaders.setContentLength(fileSystemResource.contentLength());
        requestHeaders.setContentDispositionFormData("media", fileSystemResource.getFilename());
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(form, requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        LOGGER.debug("上传 [{}] temp 收到数据：{}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), AppUploadTempResponseDTO.class);
    }

    @Override
    public AppUploadImageResponseDTO uploadImage(Integer appIndex, String filePath) throws IOException {
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>(2);
        FileSystemResource fileSystemResource = new FileSystemResource(filePath);
        form.add("file", fileSystemResource);
        String url = String.format(WXWorkApiConstants.POST_UPLOAD_IMAGE_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        requestHeaders.setContentLength(fileSystemResource.contentLength());
        requestHeaders.setContentDispositionFormData("image", fileSystemResource.getFilename());
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(form, requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        LOGGER.debug("上传 [{}] image 收到数据：{}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), AppUploadImageResponseDTO.class);
    }

    @Override
    public byte[] downloadTemp(Integer appIndex, String mediaId) {
        String url = String.format(WXWorkApiConstants.GET_DOWNLOAD_TEMP_URL, getTokenCache(appIndex), mediaId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<byte[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, byte[].class);
        if (Objects.requireNonNull(responseEntity.getHeaders().getContentType()).getSubtype().equalsIgnoreCase("json")) {
            String message = String.format("下载 [%d] temp 收到数据：%s", appIndex, new String(Objects.requireNonNull(responseEntity.getBody()), StandardCharsets.UTF_8));
            LOGGER.debug(message);
            throw new WXWorkException(message);
        }
        return responseEntity.getBody();
    }

    @Override
    public byte[] downloadVoice(Integer appIndex, String mediaId) {
        String url = String.format(WXWorkApiConstants.GET_DOWNLOAD_VOICE_URL, getTokenCache(appIndex), mediaId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<byte[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, byte[].class);
        if (Objects.requireNonNull(responseEntity.getHeaders().getContentType()).getSubtype().equalsIgnoreCase("json")) {
            String message = String.format("下载 [%d] voice 收到数据：%s", appIndex, new String(Objects.requireNonNull(responseEntity.getBody()), StandardCharsets.UTF_8));
            LOGGER.debug(message);
            throw new WXWorkException(message);
        }
        return responseEntity.getBody();
    }

    @Override
    public CheckinDataResponseDTO getCheckinData(Integer openCheckinDataType, Long startTime, Long endTime, List<String> userIdList) {
        String json = GSON.toJson(new CheckinDataRequestDTO(openCheckinDataType, startTime, endTime, userIdList));
        String url = String.format(WXWorkApiConstants.POST_CHECKIN_DATA_URL, getTokenCache(-2));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(json, requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        LOGGER.debug("请求 [-2] getCheckinData 收到数据：{}", responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), CheckinDataResponseDTO.class);
    }

    @Override
    public String getTokenCache(Integer appIndex) {
        String cache = ehcacheService.getStringCache(WXWORK_ALIAS, appIndex + "_token");
        LOGGER.debug("获取 [{}] appToken 缓存 = {}", appIndex, cache);
        if (cache == null) {
            cache = getToken(appIndex);
            ehcacheService.setStringCache(WXWORK_ALIAS, appIndex + "_token", cache, 7200L);
            LOGGER.debug("设置 [{}] appToken 缓存 = {}", appIndex, cache);
        }
        return cache;
    }

    /**
     * 获取 token
     *
     * @param appIndex 索引
     * @return token
     * @author 天之蓝
     */
    public String getToken(Integer appIndex) {
        String secret;
        switch (appIndex) {
            case -2:
                // 打卡
                secret = wxWorkProperties.getCheckin().getSecret();
                break;
            case -1:
                // 通讯录
                secret = wxWorkProperties.getAddrBook().getSecret();
                break;
            default:
                // 自建应用
                secret = wxWorkProperties.getApp().getSecret()[appIndex];
                break;
        }
        String url = String.format(WXWorkApiConstants.GET_TOKEN_URL, wxWorkProperties.getCorpId(), secret);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        String message = String.format("请求 [%d] getToken 收到数据：%s", appIndex, responseEntity.getBody());
        LOGGER.debug(message);
        TokenResponseDTO responseDTO = GSON.fromJson(responseEntity.getBody(), TokenResponseDTO.class);
        if (responseDTO.getErrcode() != 0) {
            throw new WXWorkException(message);
        }
        return responseDTO.getAccess_token();
    }

    @Override
    public String getJsapiTicketCache(Integer appIndex) {
        String cache = ehcacheService.getStringCache(WXWORK_ALIAS, appIndex + "_jsapi_ticket");
        LOGGER.debug("获取 [{}] jsapiTicket 缓存 = {}", appIndex, cache);
        if (cache == null) {
            cache = getJsapiTicket(appIndex);
            ehcacheService.setStringCache(WXWORK_ALIAS, appIndex + "_jsapi_ticket", cache, 7200L);
            LOGGER.debug("设置 [{}] jsapiTicket 缓存 = {}", appIndex, cache);
        }
        return cache;
    }

    /**
     * 获取 jsapi_ticket
     *
     * @param appIndex 索引
     * @return jsapiTicket
     * @author 天之蓝
     */
    public String getJsapiTicket(Integer appIndex) {
        String url = String.format(WXWorkApiConstants.GET_JSAPI_TICKET_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        String message = String.format("请求 [%d] getJsapiTicket 收到数据：%s", appIndex, responseEntity.getBody());
        LOGGER.debug(message);
        TicketResponseDTO responseDTO = GSON.fromJson(responseEntity.getBody(), TicketResponseDTO.class);
        if (responseDTO.getErrcode() != 0) {
            throw new WXWorkException(message);
        }
        return responseDTO.getTicket();
    }

    @Override
    public String getJsapiTicketAgentCache(Integer appIndex) {
        String cache = ehcacheService.getStringCache(WXWORK_ALIAS, appIndex + "_jsapi_ticket_agent");
        LOGGER.debug("获取 [{}] jsapiTicketAgent 缓存 = {}", appIndex, cache);
        if (cache == null) {
            cache = getJsapiTicketAgent(appIndex);
            ehcacheService.setStringCache(WXWORK_ALIAS, appIndex + "_jsapi_ticket_agent", cache, 7200L);
            LOGGER.debug("设置 [{}] jsapiTicketAgent 缓存 = {}", appIndex, cache);
        }
        return cache;
    }

    /**
     * 获取 jsapi_ticket_agent
     *
     * @param appIndex 索引
     * @return jsapiTicketAgent
     * @author 天之蓝
     */
    public String getJsapiTicketAgent(Integer appIndex) {
        String url = String.format(WXWorkApiConstants.GET_JSAPI_TICKET_AGENT_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        String message = String.format("请求 [%d] getJsapiTicketAgent 收到数据：%s", appIndex, responseEntity.getBody());
        LOGGER.debug(message);
        TicketResponseDTO responseDTO = GSON.fromJson(responseEntity.getBody(), TicketResponseDTO.class);
        if (responseDTO.getErrcode() != 0) {
            throw new WXWorkException(message);
        }
        return responseDTO.getTicket();
    }

    @Override
    public boolean verifyUrl(Integer callbackIndex, String msg_signature, String timestamp, String nonce, String echostr, HttpServletResponse response) {
        try {
            String data = new WXBizMsgCrypt(
                    wxWorkProperties.getCallback().getToken()[callbackIndex],
                    wxWorkProperties.getCallback().getEncodingAesKey()[callbackIndex],
                    wxWorkProperties.getCorpId()
            ).VerifyURL(msg_signature, timestamp, nonce, echostr);
            response.getWriter().print(data);
            LOGGER.debug("响应 [{}] verifyUrl 成功", callbackIndex);
            return true;
        } catch (Exception e) {
            LOGGER.error("验证 [{}] verifyUrl 异常", callbackIndex, e);
            return false;
        }
    }

    @Override
    public Multimap<String, String> callback(Integer callbackIndex, String msg_signature, String timestamp, String nonce, String xml, HttpServletResponse response) {
        String data;
        try {
            data = new WXBizMsgCrypt(
                    wxWorkProperties.getCallback().getToken()[callbackIndex],
                    wxWorkProperties.getCallback().getEncodingAesKey()[callbackIndex],
                    wxWorkProperties.getCorpId()
            ).DecryptMsg(msg_signature, timestamp, nonce, xml);
            LOGGER.debug("解密 [{}] xml 明文：{}", callbackIndex, data);
            response.getWriter().print("success");
            LOGGER.debug("响应 [{}] callback 成功", callbackIndex);
        } catch (Exception e) {
            String message = String.format("处理 [%d] callback 异常", callbackIndex);
            LOGGER.error(message, e);
            throw new WXWorkException(message, e);
        }
        return xmlToMultimap(data);
    }

    /**
     * xml 转 map
     *
     * @param xmlStr xml 字符串
     * @return k-v（LinkedHashMultimap，k 为完全限定节点名称，例如：grandparentNodeName.parentNodeName.nodeName）
     * @author 天之蓝
     */
    public static Multimap<String, String> xmlToMultimap(String xmlStr) {
        Multimap<String, String> map = LinkedHashMultimap.create();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8))) {
            SAXReader saxReader = new SAXReader();
            saxReader.setEncoding(StandardCharsets.UTF_8.name());
            listNodes(saxReader.read(byteArrayInputStream).getRootElement(), map);
        } catch (Exception e) {
            String message = "xml 转 map 异常";
            LOGGER.error(message, e);
            throw new WXWorkException(message, e);
        }
        return map;
    }

    /**
     * 递归遍历 xml 节点
     *
     * @param node xml 节点
     * @param map  载体
     * @author 天之蓝
     */
    public static void listNodes(Element node, Multimap<String, String> map) {
        // 获取当前节点的内容
        if (StringUtils.isNotBlank(node.getTextTrim())) {
            NodeName nodeName = new NodeName(node.getName());
            getFullyQualifiedNodeName(node, nodeName);
            LOGGER.trace("{} -> {}", nodeName.getNodeName(), node.getText());
            map.put(nodeName.getNodeName(), node.getText());
        }
        // 迭代该节点的所有子节点
        List<Element> elements = node.elements();
        for (Element element : elements) {
            // 使用递归
            listNodes(element, map);
        }
    }

    /**
     * 递归获取完全限定节点名称，以英文点“.”分隔（例如：grandparentNodeName.parentNodeName.nodeName）
     *
     * @param node     节点
     * @param nodeName 自定义节点名称对象
     * @author 天之蓝
     */
    public static void getFullyQualifiedNodeName(Element node, NodeName nodeName) {
        if (node.getParent() == null) {
            return;
        }
        nodeName.setNodeName(node.getParent().getName() + "." + nodeName.getNodeName());
        getFullyQualifiedNodeName(node.getParent(), nodeName);
    }

    /**
     * 自定义节点名称对象
     *
     * @author 天之蓝
     */
    @AllArgsConstructor
    @Data
    @NoArgsConstructor
    public static class NodeName {
        /**
         * 节点名称
         */
        private String nodeName;
    }

    @Override
    public ConfigSignatureResponseDTO getConfigSignature(Integer appIndex, String currentPageUrl) {
        String ticket = getJsapiTicketCache(appIndex);
        String nonceStr = UUID.randomUUID().toString();
        long timestamp = System.currentTimeMillis() / 1000;
        currentPageUrl = dealCurrentPageUrl(currentPageUrl);
        String str = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + currentPageUrl;
        String signature = MathUtils.sha1Hex(str);
        ConfigSignatureResponseDTO responseDTO = new ConfigSignatureResponseDTO();
        responseDTO.setAppId(wxWorkProperties.getCorpId());
        responseDTO.setTimestamp(String.valueOf(timestamp));
        responseDTO.setNonceStr(nonceStr);
        responseDTO.setSignature(signature);
        responseDTO.setJsApiList(Lists.newArrayList("所有JS接口列表：https://work.weixin.qq.com/api/doc/90000/90136/90507/"));
        LOGGER.debug("请求 [{}] getConfigSignature 返回结果：{}", appIndex, responseDTO);
        return responseDTO;
    }

    @Override
    public AgentConfigSignatureResponseDTO getAgentConfigSignature(Integer appIndex, String currentPageUrl) {
        String ticket = getJsapiTicketAgentCache(appIndex);
        String nonceStr = UUID.randomUUID().toString();
        long timestamp = System.currentTimeMillis() / 1000;
        currentPageUrl = dealCurrentPageUrl(currentPageUrl);
        String str = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + currentPageUrl;
        String signature = MathUtils.sha1Hex(str);
        AgentConfigSignatureResponseDTO responseDTO = new AgentConfigSignatureResponseDTO();
        responseDTO.setCorpid(wxWorkProperties.getCorpId());
        responseDTO.setAgentid(wxWorkProperties.getApp().getAgentId()[appIndex]);
        responseDTO.setTimestamp(String.valueOf(timestamp));
        responseDTO.setNonceStr(nonceStr);
        responseDTO.setSignature(signature);
        responseDTO.setJsApiList(Lists.newArrayList("所有JS接口列表：https://work.weixin.qq.com/api/doc/90000/90136/90507/"));
        LOGGER.debug("请求 [{}] getAgentConfigSignature 返回结果：{}", appIndex, responseDTO);
        return responseDTO;
    }

    /**
     * 处理当前页面 url
     *
     * @param currentPageUrl 当前页面 url
     * @return 字符串
     * @author 天之蓝
     */
    public static String dealCurrentPageUrl(String currentPageUrl) {
        currentPageUrl = currentPageUrl.split("#")[0];
        int index = currentPageUrl.length() - 1;
        if (currentPageUrl.lastIndexOf("/") == index) {
            currentPageUrl = currentPageUrl.substring(0, index);
        }
        return currentPageUrl;
    }

    @Override
    public CallbackIpResponseDTO getCallbackIp(Integer appIndex) {
        String url = String.format(WXWorkApiConstants.GET_CALLBACK_IP_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.debug("请求 [{}] getCallbackIp 收到数据：{}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), CallbackIpResponseDTO.class);
    }

    @Override
    public BotMsgResponseDTO sendBotTextMsg(Integer botIndex, BotTextMsgRequestDTO object) {
        HttpEntity<String> responseEntity = sendBotMsg(botIndex, GSON.toJson(object));
        String mentioned = Arrays.toString(new List[]{object.getText().getMentioned_list(), object.getText().getMentioned_mobile_list()});
        LOGGER.debug("发送 [{}] 机器人文本消息提醒 {} 收到数据：{}", botIndex, mentioned, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), BotMsgResponseDTO.class);
    }

    @Override
    public BotMsgResponseDTO sendBotMarkdownMsg(Integer botIndex, BotMarkdownMsgRequestDTO object) {
        HttpEntity<String> responseEntity = sendBotMsg(botIndex, GSON.toJson(object));
        LOGGER.debug("发送 [{}] 机器人 markdown 消息收到数据：{}", botIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), BotMsgResponseDTO.class);
    }

    /**
     * 发送机器人消息
     *
     * @param botIndex 索引
     * @param json     JSON
     * @return {@link HttpEntity}
     * @author 天之蓝
     */
    public HttpEntity<String> sendBotMsg(Integer botIndex, String json) {
        String url = wxWorkProperties.getBot().getWebhookUrl()[botIndex];
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(json, requestHeaders);
        return restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
    }
}
